using System;
using Tarkin.InfrastructureLibrary.Core.PrimitiveTypes;

namespace Tarkin.InfrastructureLibrary.Core.Basic
{
    /// <summary>
    /// The Property class is the implementation of the Property metaclass class as defined by 
    /// section 10.2.5 of the Unified Modeling Language: Infrastructure specfication (version 
    /// 2.1.1, dated 07-02-06).
    /// 
    /// NOTE - Due to C#'s single inheritance limitation, the Property class leverages
    ///        containment to simulate inheriting from the TypedElement class.
    /// </summary>
    /// <remarks>Tarkin classes are NOT thread-safe.</remarks>
    public class Property : MultiplicityElement
    {

#region Private Members

        /// <summary>
        /// Contained TypedElement instance.
        /// </summary>
        private TypedElement typedElement = null;

        /// <summary>
        /// The class that owns the Property. 
        /// 
        /// NOTE - The UML: Infrastructure spec has this property named "class" which is a C# keyword.
        /// </summary>
        private Class owningClass = null;

        /// <summary>
        /// A string that is evaluated to give a default value for the attribute when an object of the owning class is instantiated.
        /// 
        /// NOTE - The UML: Infrastructure spec has this property named "default" which is a C# keyword.
        /// </summary>
        private string defaultValue = string.Empty;

        /// <summary>
        /// Whether the object containing the attribute is a container for the object or value contained in the attribute.
        /// </summary>
        private bool isComposite = false;   

        /// <summary>
        /// Whether the attribute's value is derived from information elsewhere.
        /// </summary>
        private bool isDerived = false;
        
        /// <summary>
        /// Whether the attribute can be written to after initialization.
        /// </summary>
        private bool isReadOnly = false;

        /// <summary>
        /// Contains an opposite Property, if one exists.
        /// </summary>
        private Property opposite = null;

#endregion


#region Private Properties

        /// <summary>
        /// Contained TypedElement instance.
        /// </summary>
        private TypedElement TypedElement
        {
            get
            {
                return typedElement;
            }

            set
            {
                if ( value == null )
                {
                    throw new ArgumentNullException ( "Typed Element cannont be null." );
                }

                typedElement = value;
            }
        }

#endregion


#region constructors

        /// <summary>
        /// Overloaded constructor.
        /// </summary>
        /// <param name="typedElement">The TypedElement portion of the Property class.</param>
        public Property ( TypedElement typedElement )
        {
            TypedElement = typedElement;
        }
        
        /// <summary>
        /// Overloaded constructor.
        /// </summary>
        /// <param name="typedElement">The TypedElement portion of the Property class.</param>
        /// <param name="multiplicityElement">The MultiplicityElement of the Property class.</param>
        public Property ( TypedElement typedElement, 
                          MultiplicityElement multiplicityElement ) : base ( multiplicityElement )
        {
            TypedElement = typedElement;
        }

        /// <summary>
        /// Overloaded constructor.
        /// </summary>
        /// <param name="typedElement">The TypedElement portion of the Property class.</param>
        /// <param name="isOrdered">Whether the MultiplicityElement is ordered or not.</param>
        /// <param name="isUnique">Whether the MultiplicityElement is unique or not.</param>
        /// <param name="lower">The lower value of the MultiplicityElement.</param>
        /// <param name="upper">The upper value of the MultiplicityElement.</param>
        public Property ( TypedElement typedElement,
                          bool isOrdered,
                          bool isUnique,
                          uint lower,
                          UnlimitedNatural upper ) : base ( isOrdered,
                                                            isUnique,
                                                            lower,
                                                            upper )
        {
            TypedElement = typedElement;
        }

        /// <summary>
        /// Overloaded constructor.
        /// </summary>
        /// <param name="typedElement">The TypedElement portion of the Property class.</param>
        /// <param name="multiplicityElement">The MultiplicityElement of the Property class.</param>
        /// <param name="owningClass">The class that owns the property.</param>
        /// <param name="defaultValue">A string that is evaluated to give a default value for the attribute when an object of the owning class is instantiated.</param>
        /// <param name="isComposite">Whether the object containing the attribute is a container for the object or value contained in the attribute.</param>
        /// <param name="isDerived">Whether the attribute's value is derived from information elsewhere.</param>
        /// <param name="isReadOnly">Whether the attribute can be written to after initialization.</param>
        /// <param name="opposite">Contains an opposite Property, if one exists.</param>
        public Property ( TypedElement typedElement,
                          MultiplicityElement multiplicityElement,
                          Class owningClass,
                          string defaultValue,
                          bool isComposite,
                          bool isDerived,
                          bool isReadOnly,
                          Property opposite ) : base ( multiplicityElement )
        {
            TypedElement = typedElement;
            Class = owningClass;
            Default = defaultValue;
            IsComposite = isComposite;
            IsDerived = isDerived;
            IsReadOnly = isReadOnly;
            Opposite = opposite;
        }

#endregion


#region Public Properties

        /// <summary>
        /// The class that owns the property. 
        /// </summary>
        public Class Class
        {
            get
            {
                return owningClass;
            }

            set
            {
                owningClass = value;
            }
        }

        /// <summary>
        /// A string that is evaluated to give a default value for the attribute when an object of the owning class is instantiated.
        /// </summary>
        public string Default
        {
            get
            {
                return defaultValue;
            }

            set
            {
                defaultValue = value;
            }
        }

        /// <summary>
        /// Whether the object containing the attribute is a container for the object or value contained in the attribute.
        /// </summary>
        public bool IsComposite
        {
            get
            {
                return isComposite;
            }

            set
            {
                isComposite = value;    
            }
        }

        /// <summary>
        /// Whether the attribute's value is derived from information elsewhere.
        /// </summary>
        public bool IsDerived
        {
            get
            {
                return isDerived;
            }

            set
            {
                isDerived = value;  
            }
        }

        /// <summary>
        /// Whether the attribute can be written to after initialization.
        /// </summary>
        public bool IsReadOnly
        {
            get
            {
                return isReadOnly;
            }

            set
            {
                isReadOnly = true;
            }
        }

        /// <summary>
        /// Contains an opposite Property, if one exists.
        /// </summary>
        public Property Opposite
        {
            get
            {
                return opposite;
            }

            set
            {
                opposite = value;
            }
        }

        /// <summary>
        /// The Type of the Property.
        /// </summary>
        public Type Type
        {
            get
            {
                return TypedElement.Type;
            }

            set
            {
                TypedElement.Type = value;
            }
        }

        /// <summary>
        /// The name of the Property.
        /// </summary>
        public string Name
        {
            get
            {
                return TypedElement.Name;
            }

            set
            {
                TypedElement.Name = value;
            }
        }

#endregion

    }
}
